package com.hundsun.epay.pay.certkey;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Ar.M
 * 
 *         TODO 要更改此生成的类型注释的模板，请转至 窗口 － 首选项 － Java － 代码样式 － 代码模板
 */
public class CertUtils {
	/*concurrency bug fixed begins*/
	public static ConcurrentHashMap<String, PublicKey> publicKeyMap = new ConcurrentHashMap<String, PublicKey>();
	public static ConcurrentHashMap<String, byte[]> privatekeyPathByteMap = new ConcurrentHashMap<String, byte[]>();
	/*concurrency bug fixed ends*/


	/**
	 * 
	 * @Method: getPublicKeyByCer4Merchant
	 * @Description:商户取得江苏银行公钥,第一次读取后放入内存中
	 * @param keypath
	 * @return
	 * @throws GetKeyException
	 */
	public static PublicKey getPublicKeyByCer4Merchant(String keypath)
			throws GetKeyException {
		/*concurrency bug fixed begins*/
		PublicKey publicKey = null,tempPublicKey = null;
		/*concurrency bug fixed ends*/

		FileInputStream fis = null;
		try {
			publicKey = publicKeyMap.get(keypath);
			if (publicKey == null) {
				fis = new FileInputStream(keypath);
				// 证书类型 X.509
				CertificateFactory certFactory = CertificateFactory
						.getInstance("X.509");
				// 获得证书并初始化
				X509Certificate cert = (X509Certificate) certFactory
						.generateCertificate(fis);
				// 根据证书获得公钥
				publicKey = cert.getPublicKey();
				/*concurrency bug fixed begins*/
				tempPublicKey = publicKeyMap.putIfAbsent(keypath, publicKey);
				if(tempPublicKey!=null)publicKey = tempPublicKey;
				/*concurrency bug fixed ends*/

			}
		} catch (FileNotFoundException e) {
			throw new GetKeyException("证书文件不存在", e);
		} catch (CertificateException e) {
			System.out.println(e.getMessage());
			throw new GetKeyException("证书错误", e);
		} finally {
			try {
				if (fis != null)
					fis.close();
			} catch (IOException e) {
				System.out.println(e.getMessage());
				throw new GetKeyException("读写证书文件错误", e);
			}
		}
		return publicKey;
	}

	/**
	 * 根据证书获得公钥（提供江苏银行使用）
	 * 
	 * @param path
	 *            证书存储路径
	 * @return PublicKey
	 * @throws GetKeyException
	 */
	public static PublicKey getPublicKeyByCer4Jschina(String path)
			throws GetKeyException {

		PublicKey publicKey = null;
		FileInputStream fis = null;
		try {
			// 证书类型 X.509
			CertificateFactory certFactory = CertificateFactory
					.getInstance("X.509");
			fis = new FileInputStream(path);
			// 获得证书并初始化
			X509Certificate cert = (X509Certificate) certFactory
					.generateCertificate(fis);
			// 根据证书获得公钥
			publicKey = cert.getPublicKey();
		} catch (FileNotFoundException e) {
			System.out.println(e.getMessage());
			throw new GetKeyException("证书文件不存在", e);
		} catch (CertificateException e) {
			System.out.println(e.getMessage());
			throw new GetKeyException("证书错误", e);
		} finally {
			try {
				fis.close();
			} catch (IOException e) {
				System.out.println(e.getMessage());
				throw new GetKeyException("读写证书文件错误", e);
			}
		}

		return publicKey;
	}

	public static PublicKey getPublicKeyByPfx(String path, String pwd)
			throws GetKeyException {

		PublicKey publicKey = null;
		final String KEYSTORE_ALIAS = "alias";
		FileInputStream fis = null;

		try {
			KeyStore ks = KeyStore.getInstance("PKCS12");
			fis = new FileInputStream(path);

			char[] nPassword = null;
			if ((pwd == null) || pwd.trim().equals("")) {
				nPassword = null;
			} else {
				nPassword = pwd.toCharArray();
			}

			ks.load(fis, nPassword);
			// fis.close();

			Enumeration en = ks.aliases();
			String keyAlias = null;
			if (en.hasMoreElements()) {
				keyAlias = (String) en.nextElement();
			}

			Certificate cert = ks.getCertificate(keyAlias);
			publicKey = cert.getPublicKey();
		} catch (KeyStoreException e) {
			throw new GetKeyException(
					"Error Number:-100000, Error Description: ER_GET_KEY_ERROR(获取KeyStore失败)",
					e);
		} catch (FileNotFoundException e) {
			throw new GetKeyException(
					"Error Number:-100001, Error Description: ER_GET_KEY_ERROR(证书文件不存在)",
					e);
		} catch (CertificateException e) {
			throw new GetKeyException(
					"Error Number:-100002, Error Description: ER_GET_KEY_ERROR(证书错误)",
					e);
		} catch (NoSuchAlgorithmException e) {
			throw new GetKeyException(
					"Error Number:-100003, Error Description: ER_GET_KEY_ERROR(无效算法错误)",
					e);
		} catch (IOException e) {
			throw new GetKeyException(
					"Error Number:-100004, Error Description: ER_GET_KEY_ERROR(读写证书文件错误)",
					e);
		} finally {
			try {
				fis.close();
			} catch (IOException e) {
				throw new GetKeyException(
						"Error Number:-100006, Error Description: ER_GET_KEY_ERROR(读写证书文件错误)",
						e);
			}
		}
		return publicKey;
	}

	public static byte[] getPrivateKeyPath(String path)
			throws GetKeyException {
		/*concurrency bug fixed begins*/
		byte[] buffer=null,tempBuffer = null;
		/*concurrency bug fixed ends*/

		FileInputStream fis=null;
		try {
			buffer = privatekeyPathByteMap.get(path);
			
			if (buffer == null) {
				fis = new FileInputStream(path);
				
				buffer = new byte[fis.available()];
				fis.read(buffer);
				
				/*concurrency bug fixed begins*/
				tempBuffer = privatekeyPathByteMap.putIfAbsent(path, buffer);
				if(tempBuffer!=null)buffer=tempBuffer;
				/*concurrency bug fixed ends*/

			}
		} catch (FileNotFoundException e) {
			throw new GetKeyException(
					"Error Number:-100001, Error Description: ER_GET_KEY_ERROR(证书文件不存在)",
					e);
		}catch (IOException e) {
			throw new GetKeyException(
					"Error Number:-100001, Error Description: ER_GET_KEY_ERROR(证书文件不存在)",
					e);
		}finally{
			
				try {
					if(fis!=null)
					fis.close();
				} catch (IOException e) {
					throw new GetKeyException(
							"Error Number:-100006, Error Description: ER_GET_KEY_ERROR(读写证书文件错误)",
							e);
				}
		}
		return buffer;
	}

	/**
	 * 密钥 KeyStore
	 * 
	 * @param path
	 * @param pwd
	 * @return
	 * @throws GetKeyException
	 */
	public static PrivateKey getPrivateKeyByPfx(String path, String pwd)
			throws GetKeyException {

		PrivateKey privateKey = null;
		final String KEYSTORE_ALIAS = "alias";
		ByteArrayInputStream fis = null;

		try {
			
			byte[] buffer = getPrivateKeyPath(path);
			
			
			fis=new ByteArrayInputStream(buffer);
			KeyStore ks = KeyStore.getInstance("PKCS12");// pkcs12格式（.pfx后缀）

			char[] nPassword = null;
			if ((pwd == null) || pwd.trim().equals("")) {
				nPassword = null;
			} else {
				nPassword = pwd.toCharArray();
			}

			ks.load(fis, nPassword);

			Enumeration en = ks.aliases();
			String keyAlias = null;
			if (en.hasMoreElements()) {
				keyAlias = (String) en.nextElement();
			}

			privateKey = (PrivateKey) ks.getKey(keyAlias, nPassword);
		} catch (KeyStoreException e) {
			throw new GetKeyException(
					"Error Number:-100000, Error Description: ER_GET_KEY_ERROR(获取KeyStore失败)",
					e);
		} catch (FileNotFoundException e) {
			throw new GetKeyException(
					"Error Number:-100001, Error Description: ER_GET_KEY_ERROR(证书文件不存在)",
					e);
		} catch (CertificateException e) {
			throw new GetKeyException(
					"Error Number:-100002, Error Description: ER_GET_KEY_ERROR(证书错误)",
					e);
		} catch (NoSuchAlgorithmException e) {
			throw new GetKeyException(
					"Error Number:-100003, Error Description: ER_GET_KEY_ERROR(无效算法错误)",
					e);
		} catch (IOException e) {
			throw new GetKeyException(
					"Error Number:-100004, Error Description: ER_GET_KEY_ERROR(读写证书文件错误)",
					e);
		} catch (UnrecoverableKeyException e) {
			throw new GetKeyException(
					"Error Number:-100005, Error Description: ER_GET_KEY_ERROR(无权读取证书密钥错误)",
					e);
		} finally {
			try {
				if(fis!=null)
					fis.close();
			} catch (IOException e) {
				throw new GetKeyException(
						"Error Number:-100006, Error Description: ER_GET_KEY_ERROR(读写证书文件错误)",
						e);
			}
		}
		return privateKey;
	}

	/**
	 * 验证数字证书当前是否有效
	 * 
	 * @param certificatePath
	 *            证书存储路径
	 * @return
	 */
	public static boolean verifyCertificate(String certificatePath) {
		Certificate certificate;
		try {
			certificate = getCertificate(certificatePath);
			return verifyCertificate(certificate);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * * 验证数字证书是在给定的日期是否有效
	 * 
	 * @param date
	 *            日期
	 * @param certificatePath
	 *            证书存储路径
	 * @return
	 */
	public static boolean verifyCertificate(Date date, String certificatePath) {
		Certificate certificate;
		try {
			certificate = getCertificate(certificatePath);
			return verifyCertificate(date, certificate);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 获得证书
	 * 
	 * @param certificatePath
	 *            证书存储路径
	 * @return
	 * @throws Exception
	 */
	private static Certificate getCertificate(String certificatePath)
			throws GetKeyException {
		FileInputStream in = null;
		Certificate certificate = null;
		try {
			CertificateFactory certificateFactory = CertificateFactory
					.getInstance("X.509");
			in = new FileInputStream(certificatePath);
			certificate = certificateFactory.generateCertificate(in);
		} catch (FileNotFoundException e) {
			throw new GetKeyException("证书文件不存在", e);
		} catch (CertificateException e) {
			throw new GetKeyException("证书错误", e);
		} finally {
			try {
				in.close();
			} catch (IOException e) {
				System.out.println(e.getMessage());
				throw new GetKeyException("读写证书文件错误", e);
			}
		}
		return certificate;
	}

	/**
	 * 校验证书当前是否有效
	 * 
	 * @param certificate
	 *            证书
	 * @return
	 */
	public static boolean verifyCertificate(Certificate certificate)
			throws GetKeyException {
		return verifyCertificate(new Date(), certificate);
	}

	/**
	 * 验证证书是否过期或无效
	 * 
	 * @param date
	 *            日期
	 * @param certificate
	 *            证书
	 * @return
	 */
	public static boolean verifyCertificate(Date date, Certificate certificate)
			throws GetKeyException {
		boolean isValid = true;

		X509Certificate x509Certificate = (X509Certificate) certificate;
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		try {
			x509Certificate.checkValidity(date);
		} catch (CertificateExpiredException e) {
			isValid = false;
			// 证书过期异常。只要当前的 Date 或指定的 Date 超过证书有效期中所指定的 notAfter 日期/时间，就抛出此异常。
			throw new GetKeyException(
					"Error Number:-100007, Error Description: ER_GET_KEY_ERROR(证书已过有效期)"
							+ format.format(x509Certificate.getNotAfter()), e);
		} catch (CertificateNotYetValidException e) {
			isValid = false;
			format.format(x509Certificate.getNotBefore());
			// 证书尚无效异常。只要当前的 Date 或指定的 Date 在证书有效期中的 notBefore 日期/时间之前，就抛出此异常。
			throw new GetKeyException(
					"Error Number:-100008, Error Description: ER_GET_KEY_ERROR(证书未激活)"
							+ format.format(x509Certificate.getNotBefore()), e);
		}

		return isValid;
	}

	/**
	 * 动态生成key
	 * 
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws Exception
	 */
	public static KeyPair getKey() throws GetKeyException {
		KeyPair keyPair = null;
		try {
			KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
			int keysize = 1024;
			// 设置公钥/私钥对的长度
			keyGen.initialize(keysize);
			// 生成一个DSA算法的公钥/私钥
			keyPair = keyGen.generateKeyPair();
		} catch (NoSuchAlgorithmException e) {
			throw new GetKeyException(
					"Error Number:-100008, Error Description: ER_GET_KEY_ERROR(动态生成DSA失败)",
					e);
		}
		return keyPair;
	}

	public static void main(String[] args) throws Exception {
		System.out.println("公钥信息："
				+ CertUtils.getPublicKeyByCer4Jschina("c:/key/merctest.cer"));

		System.out.println("密钥信息："
				+ CertUtils.getPrivateKeyByPfx("c:/key/merctest.pfx",
						"12345678"));
		/* 证书RSA算法需转换成RSAPrivateKey */
		RSAPrivateKey privateKey = (RSAPrivateKey) CertUtils
				.getPrivateKeyByPfx("c:/key/merctest.pfx", "12345678");
		// 获得系数、
		System.out.println(privateKey.getModulus());
		/* 证书是否过有效期 */
		System.out.println(verifyCertificate("c:/key/merctest.cer"));
	}
}
